#include "mod-udp.hpp"

#define DEBUG false

Record * UdpDecoder::decode (Packet & pkt, Record & prev)
{
  UdpRecord &r = this->rec;
  UdpSession * ssn;
  UdpHdr * hdr = (UdpHdr*) pkt.getCur ();
  const int hdrLen = sizeof (UdpHdr);
  
  debug (DEBUG, "Etner UDP decoder");
  if (NULL == hdr || ! pkt.moveCur (hdrLen) || ! r.lock ())
    return NULL;

  r.setData (DEC_UDP, hdr, hdrLen);
  r.sport = ntohs (hdr->uhSrcPort);
  r.dport = ntohs (hdr->uhDstPort);
  ssn = fetchUdpSession (prev, r.sport, r.dport);
  r.setSession (ssn);

  debug (DEBUG, "Leave UDP decoder");
  return &(this->rec);
}

UdpSession * UdpDecoder::fetchUdpSession 
(Record &prev, u_int16_t &sport, u_int16_t &dport)
{
  UdpSessionRoot * root = NULL;
  UdpSession * ssn;
  void * ssnKey;

  if (prev.getSessionRootPtr ())
    {
      if (NULL == (root = (UdpSessionRoot*)prev.getSessionRoot ()))
	{
	  bool ret;
	  root = new UdpSessionRoot ();
	  ret = prev.setSessionRoot (root);
	  assert (ret);
	}
    }

  ssnKey = this->makeSsnKey (&sport, &dport, 2);
  if (NULL == (ssn = dynamic_cast<UdpSession*>
	       (root->ssn.lookup (ssnKey, 4))))
    {
      ssn = new UdpSession ();
      ssn->joinTable (root->ssn, ssnKey, 4);
    }

  ssn->setTimeout (this->getTimer (), SSN_TIMEOUT);

	//  ssn = root->ssn.fetch (ssnKey, 4);
	//  if (ssn->tnode)
	//    {
	//      this->timer->resetTimer (ssn->tnode, SSN_TIMEOUT);
	//    }
	//  else
	//    {
	//      ssn->setKey (ssnKey, 4);
	//      ssn->tree = &root->ssn;
	//      ssn->tnode = this->timer->setTimer (UdpDecoder::timeoutSession, ssn, SSN_TIMEOUT);
	//    }
	//
  return ssn;
}


NDecFilter * UdpDecoder::makeFilter (string &name)
{
  UdpFilter * f = NULL;

  if (name == "udp.src_port")
    f = new UdpFilter (UdpFilter::FT_SRC_PORT);  
  else if (name == "udp.dst_port")
    f = new UdpFilter (UdpFilter::FT_DST_PORT);  

  return f;  
}

NDecMpxFilter * UdpDecoder::makeMpxFilter (string &name)
{
  UdpMpxFilter * f = NULL;

  if (name == "udp.src_port")
    f = new UdpMpxFilter (UdpMpxFilter::FT_SRC_PORT);  
  else if (name == "udp.dst_port")
    f = new UdpMpxFilter (UdpMpxFilter::FT_DST_PORT);  

  return f;  
}

//==========================================================


bool UdpFilter::setup (string &arg)
{
  if (this->type == FT_SRC_PORT || this->type == FT_DST_PORT)
    {
      unsigned int n;
      char * e = NULL;
      n = strtoul (arg.c_str (), &e, 0);
      if (*e != '\0' || n > 0xffff)
	{
	  this->setErrMsg ("[UdpFilter] Invalid port number '" + arg + "'");
	  return false;
	}

      this->port = static_cast <u_int16_t> (n);
    }

  return true;
}

void UdpFilter::check (NDecResult &res)
{
  u_int16_t * tgt = NULL;
  UdpRecord * rp = dynamic_cast<UdpRecord*> (res.getRecord (DEC_UDP));
  if (rp == NULL)
    return ;
  
  switch (this->type)
    {
    case FT_SRC_PORT: tgt = &rp->sport; break;
    case FT_DST_PORT: tgt = &rp->dport; break;
    default:
      assert (0);
    }

  if (this->port == 0 || *tgt == this->port)
    this->pushResult (tgt, 2);

  return ;
}



//==========================================================

bool UdpMpxFilter::setup (string &arg, void * p)
{
  char * e = NULL;
  unsigned int n;
  n = strtoul (arg.c_str (), &e, 0);
  if (*e != '\0' || n > 0xffff)
    {
      //      this->setErrMsg ("udp.(src|dst)_port: expected digit \"" +  arg + "\"");
      return false;
    }

  u_int16_t port = static_cast <u_int16_t> (n);	
  NDecMpxRes * res = this->portTable.fetch (&port, sizeof (port));
  res->addItem (p);
  return true;
}


int UdpMpxFilter::exec (NDecResult &res)
{
  u_int16_t * tgt = NULL;
  NDecMpxRes * mr;
  UdpRecord * rp = dynamic_cast<UdpRecord*> (res.getRecord (DEC_UDP));
  if (NULL == rp)
    return false;
  
  tgt = (this->type == FT_SRC_PORT) ? &rp->dport : &rp->sport;
  if (NULL == (mr = this->portTable.lookup (tgt, sizeof (u_int16_t))))
    return 0;

  this->setResult (mr);
  return mr->getCount ();
}
